home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / pop3serv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-08  |  19.8 KB  |  814 lines

  1. /*              POP3 Server state machine - see RFC 1460
  2.  *
  3.  *    Jan 92    Erik Olson olson@phys.washington.edu
  4.  *        Taken from POP2 server code in NOS 910618
  5.  *        Rewritten/converted to POP3
  6.  *    Feb 92    William Allen Simpson
  7.  *        integrated with current work
  8.  *      Aug 94  ported to WNOS (DG1ZX)
  9.  *
  10.  *  "Need-to" list: XTND XMIT (to get WinQVTnet to work)
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <fcntl.h>
  15. #include <time.h>
  16. #include <sys/stat.h>
  17. #ifdef UNIX
  18. #include <sys/types.h>
  19. #endif
  20. #if    defined(__STDC__) || defined(__TURBOC__)
  21. #include <stdarg.h>
  22. #endif
  23. #include <ctype.h>
  24. #include <setjmp.h>
  25.  
  26. #include "global.h"
  27. #include "config.h"
  28.  
  29. #ifdef POP3_SERVER
  30.  
  31. #include "mbuf.h"
  32. #include "cmdparse.h"
  33. #include "socket.h"
  34. #include "proc.h"
  35. #include "files.h"
  36. #include "smtp.h"
  37. #include "dirutil.h"
  38.  
  39.  
  40. #define LOG 1
  41. #define BUF_LEN    1024
  42.  
  43. /* ---------------- common server data structures ---------------- */
  44. /* POP message pointer element */
  45.  
  46. struct pop_msg {
  47.   long len;
  48.   long pos;
  49.   int deleted;
  50.   struct pop_msg *next;
  51. };
  52.  
  53. /* POP server control block */
  54.  
  55. struct pop_scb {
  56.   int socket;            /* socket number for this connection */
  57.   char state;            /* server state */
  58. #define      LSTN    0
  59. #define      AUTH    1
  60. #define      TRANS    2
  61. #define      UPDATE    3
  62. #define      DONE    5
  63.   char    buf[BUF_LEN];        /* input line buffer */
  64.   char    count;            /* line buffer length */
  65.   char    username[64];        /* user/folder name */
  66.   FILE    *wf;            /* work folder file pointer */
  67.   int    folder_len;        /* number of msgs in current folder */
  68.   int    high_num;        /* highest message number accessed */
  69.   long    folder_file_size;     /* length of the current folder file, in bytes */
  70.   char    folder_modified;    /* mail folder contents modified flag */
  71.   struct pop_msg *msg;        /* message database link-list */
  72. };
  73.  
  74. #define NULLSCB  (struct pop_scb *)0
  75.  
  76. /* Response messages */
  77.  
  78. static char count_rsp[]        = "+OK you have %d messages\n",
  79.         error_rsp[]        = "-ERR %s\n",
  80.         greeting_msg[]    = "+OK %s POP3 ready\n",
  81.         user_rsp[]        = "+OK user\n",
  82. /*         pass_rsp[]        = "+OK password\r\n", */
  83.         stat_rsp[]        = "+OK %d %ld\n",
  84.         list_single_rsp[]    = "+OK %d %ld\n",
  85.         list_multi_rsp[]    = "+OK %d messages (%ld octets)\n",
  86.         retr_rsp[]        = "+OK %ld octets\n",
  87.         multi_end_rsp[]    = ".\n",
  88.         dele_rsp[]        = "+OK message %d deleted\n",
  89.         noop_rsp[]        = "+OK\n",
  90.         last_rsp[]        = "+OK %d\n",
  91.         signoff_msg[]    = "+OK %s POP3 server signing off\n";
  92.  
  93.  
  94. static void near delete_scb __ARGS((struct pop_scb *scb));
  95. static void pop3serv __ARGS((int s,void *unused,void *p));
  96. static int  near poplogin __ARGS((char *pass,char *username));
  97. static void near pop3_sm __ARGS((struct pop_scb *scb));
  98.  
  99. static int Spop = -1; /* prototype socket for service */
  100.  
  101.  
  102. /* Start up POP3 receiver service */
  103. int
  104. pop3start(int argc,char *argv[],void *p) {
  105.   struct sockaddr_in lsocket;
  106.   int s;
  107.  
  108.   if (Spop != -1) {
  109.     return 0;
  110.   }
  111.  
  112.   psignal(Curproc,0);    /* Don't keep the parser waiting */
  113.   chname(Curproc,"POP3 listener");
  114.  
  115.   lsocket.sin_family = AF_INET;
  116.   lsocket.sin_addr.s_addr = INADDR_ANY;
  117.   lsocket.sin_port = (argc < 2) ? IPPORT_POP3 : atoi(argv[1]);
  118.  
  119.   Spop = socket(AF_INET,SOCK_STREAM,0);
  120.   bind(Spop,(char *)&lsocket,sizeof(lsocket));
  121.  
  122.   listen(Spop,1);
  123.  
  124.   for (;;) {
  125.     if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  126.       break;        /* Service is shutting down */
  127.  
  128.     if (availmem() < Memthresh) {
  129.       usputs(s,Nospace);
  130.       shutdown(s,1);
  131.     } else {
  132.      /* Spawn a server */
  133.      sockmode(s,SOCK_ASCII);
  134.      newproc("POP3 server",2048,pop3serv,s,NULL,NULL,0);
  135.    }
  136.   }
  137.   return 0;
  138. }
  139.  
  140. /* Shutdown POP3 service (existing connections are allowed to finish) */
  141. int
  142. pop3stop(int argc,char *argv[],void *p) {
  143.   close_s(Spop);
  144.   Spop = -1;
  145.   return 0;
  146. }
  147.  
  148. static void
  149. pop3serv(int s,void *unused,void *p) {
  150.   struct pop_scb *scb;
  151.  
  152.   sockowner(s,Curproc);        /* We own it now */
  153.   log(s,"open POP3");
  154.  
  155.   if((scb = (struct pop_scb *)mxallocw(sizeof(struct pop_scb))) == NULLSCB) {
  156.     tputs(Nospace);
  157. #ifdef LOG
  158.     log(scb->socket,"close POP3- no space");
  159. #endif
  160.     close_s(s);
  161.     return;
  162.   }
  163.  
  164.   scb->username[0] = '\0';
  165.   scb->msg = NULL;
  166.   scb->wf = NULL;
  167.   scb->count = scb->folder_file_size = 0;
  168.   scb->folder_modified = FALSE;
  169.   scb->socket = s;
  170.   scb->state  = AUTH;
  171.  
  172.   usprintf(scb->socket,greeting_msg,Hostname);
  173.  
  174.   for (;;) {
  175.     if ( scb->state == DONE
  176.     || (scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  177.       /* He closed on us */
  178.       break;
  179.     }
  180.     rip(scb->buf);
  181.     if (strlen(scb->buf) == 0)    /* Ignore blank cmd lines */
  182.       continue;
  183.     pop3_sm(scb);
  184.   }
  185.  
  186.   log(scb->socket,"close POP3");
  187.   close_s(scb->socket);
  188.   delete_scb(scb);
  189. }
  190.  
  191.  
  192. /* Free msg link-list */
  193. static void near
  194. delete_msglist(struct pop_msg *b_msg) {
  195.   struct pop_msg *msg,*msg2;
  196.  
  197.   msg = b_msg;
  198.   while(msg!=NULL) {
  199.     msg2=msg->next;
  200.     xfree(msg);
  201.     msg=msg2;
  202.   }
  203. }
  204.  
  205. /* Free resources, delete control block */
  206. static void near
  207. delete_scb(struct pop_scb *scb) {
  208.   if (scb == NULLSCB)
  209.     return;
  210.   if (scb->wf != NULL)
  211.     fclose(scb->wf);
  212.   if (scb->msg  != NULL)
  213.     delete_msglist(scb->msg);
  214.   xfree((char *)scb);
  215. }
  216.  
  217.  
  218. /* --------------------- start of POP server code ------------------------ */
  219.  
  220. #define BITS_PER_WORD    16
  221.  
  222. #define isSOM(x)    ((strncmp(x,"From ",5) == 0))
  223.  
  224. /* Command string specifications */
  225.  
  226. static char
  227.         user_cmd[] = "USER ",
  228.         pass_cmd[] = "PASS ",
  229.         quit_cmd[] = "QUIT",
  230.         stat_cmd[] = "STAT",
  231.         list_cmd[] = "LIST",
  232.         retr_cmd[] = "RETR",
  233.         dele_cmd[] = "DELE",
  234.         noop_cmd[] = "NOOP",
  235.         rset_cmd[] = "RSET",
  236.         top_cmd[]  = "TOP",
  237.         last_cmd[] = "LAST";
  238.  
  239. static void near
  240. pop3_sm(struct pop_scb *scb) {
  241.   char password[40];
  242.  
  243.   /* some prototypes */
  244.   void near state_error(struct pop_scb *,char *);
  245.   void near fatal_error(struct pop_scb *,char *);
  246.   void near open_folder(struct pop_scb *);
  247.   void near do_cleanup(struct pop_scb *);
  248.   void near stat_message(struct pop_scb *);
  249.   void near list_message(struct pop_scb *);
  250.   void near retr_message(struct pop_scb *);
  251.   void near dele_message(struct pop_scb *);
  252.   void near noop_message(struct pop_scb *);
  253.   void near last_message(struct pop_scb *);
  254.   void near rset_message(struct pop_scb *);
  255.   void near top_message(struct pop_scb *);
  256.   void near close_folder(struct pop_scb *);
  257.  
  258.   if (scb == NULLSCB)     /* be certain it is good -- wa6smn */
  259.     return;
  260.  
  261.   switch(scb->state) {
  262.     case AUTH:
  263.       if (strncmp(scb->buf,user_cmd,strlen(user_cmd)) == 0){
  264.     char *cp1, *cp = scb->buf;
  265.  
  266.     while (*cp++ != ' ');        /* skip spaces */
  267.     cp1 = cp;
  268.     while (*cp++ != ' ');
  269.     *cp = '\0';
  270.     strcpy(scb->username,cp1);
  271.     usputs(scb->socket,user_rsp);
  272.  
  273.       } else if (strncmp(scb->buf,pass_cmd,strlen(pass_cmd)) == 0){
  274.       char *cp1, *cp = scb->buf;
  275.  
  276.       while (*cp++ != ' ');        /* skip spaces */
  277.       cp1 = cp;
  278.       while (*cp++ != ' ');
  279.       *cp = '\0';
  280.       strcpy(password,cp1);
  281.       if (!poplogin(scb->username,password)) {
  282. #ifdef LOG
  283.         log(scb->socket,"POP3 access DENIED to %s",scb->username);
  284. #endif
  285.         state_error(scb,"Access DENIED!!");
  286.         return;
  287.       }
  288.  
  289. #ifdef LOG
  290.       log(scb->socket,"POP3 access granted to %s",scb->username);
  291. #endif
  292.       open_folder(scb);
  293.       } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  294.        do_cleanup(scb);
  295.       } else
  296.        state_error(scb,"(AUTH) expected USER, PASS or QUIT");
  297.       break;
  298.  
  299.     case TRANS:
  300.       if (strncmp(scb->buf,stat_cmd,strlen(stat_cmd)) == 0)
  301.     stat_message(scb);
  302.  
  303.       else if (strncmp(scb->buf,list_cmd,strlen(list_cmd)) == 0)
  304.     list_message(scb);
  305.  
  306.       else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  307.     retr_message(scb);
  308.  
  309.       else if (strncmp(scb->buf,dele_cmd,strlen(dele_cmd)) == 0)
  310.     dele_message(scb);
  311.  
  312.       else if (strncmp(scb->buf,last_cmd,strlen(noop_cmd)) == 0)
  313.     noop_message(scb);
  314.  
  315.       else if (strncmp(scb->buf,last_cmd,strlen(last_cmd)) == 0)
  316.     last_message(scb);
  317.  
  318.       else if (strncmp(scb->buf,top_cmd,strlen(top_cmd)) == 0)
  319.     top_message(scb);
  320.  
  321.       else if (strncmp(scb->buf,rset_cmd,strlen(rset_cmd)) == 0)
  322.     rset_message(scb);
  323.  
  324.       else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  325.     do_cleanup(scb);
  326.  
  327.       else
  328.     state_error(scb,"(TRANS) unsupported/unrecognized command");
  329.       break;
  330.  
  331.     case DONE:
  332.       break;
  333.  
  334.     default:
  335.       fatal_error(scb,"(TOP) State Error!!");
  336.       break;
  337.     }
  338. }
  339.  
  340. static void near
  341. do_cleanup(struct pop_scb *scb) {
  342.   void near close_folder(struct pop_scb *);
  343.  
  344.   close_folder(scb);
  345.   usprintf(scb->socket,signoff_msg,Hostname);
  346.   scb->state = DONE;
  347. }
  348.  
  349. static void near
  350. state_error(struct pop_scb *scb,char *msg) {
  351.   usprintf(scb->socket,error_rsp,msg);
  352. }
  353.  
  354. static void near
  355. fatal_error(struct pop_scb *scb,char *msg) {
  356.   usprintf(scb->socket,error_rsp,msg);
  357.   scb->state = DONE;
  358. }
  359.  
  360. static void near
  361. close_folder(struct pop_scb *scb) {
  362.    char folder_pathname[MAXPATH];
  363.    char line[BUF_LEN];
  364.    FILE *fd;
  365.    int deleted = FALSE;
  366.    int msg_no = 0;
  367.    struct pop_msg *msg;
  368.    struct stat folder_stat;
  369.    int near newmail(struct pop_scb *);
  370.    void near state_error(struct pop_scb *,char *);
  371.    void near fatal_error(struct pop_scb *,char *);
  372.  
  373.  
  374.    if (scb->wf == NULL)
  375.      return;
  376.  
  377.    if (!scb->folder_modified) {
  378.      /* no need to re-write the folder if we have not modified it */
  379.      fclose(scb->wf);
  380.      scb->wf = NULL;
  381.      delete_msglist(scb->msg);
  382.      scb->msg=NULL;
  383.      return;
  384.    }
  385.  
  386.    sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  387.  
  388.    if (newmail(scb)) {
  389.      /* copy new mail into the work file and save the
  390.     message count for later */
  391.      if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE) {
  392.        fatal_error(scb,"Unable to add new mail to folder");
  393.        return;
  394.      }
  395.  
  396.      fseek(scb->wf,0,SEEK_END);
  397.      fseek(fd,scb->folder_file_size,SEEK_SET);
  398.      while (!feof(fd)) {
  399.        fgets(line,BUF_LEN,fd);
  400.        fputs(line,scb->wf);
  401.        pwait(NULL);
  402.      }
  403.      fclose(fd);
  404.    }
  405.  
  406.    /* now create the updated mail folder */
  407.  
  408.    if ((fd = open_file(folder_pathname,"w",0,1)) == NULLFILE){
  409.      fatal_error(scb,"Unable to update mail folder");
  410.      return;
  411.    }
  412.  
  413.    rewind(scb->wf);
  414.    msg=scb->msg;
  415.    while (!feof(scb->wf)){
  416.      fgets(line,BUF_LEN,scb->wf);
  417.  
  418.      if (isSOM(line)){
  419.        if (msg!=NULL)
  420.      msg=msg->next;
  421.        msg_no++;
  422.        if (msg!=NULL)
  423.      deleted = msg->deleted;
  424.        else
  425.      deleted = FALSE;
  426.      }
  427.  
  428.      if (deleted)
  429.        continue;
  430.      fputs(line,fd);
  431.    }
  432.  
  433.    fclose(fd);
  434.  
  435.    if (!stat(folder_pathname,&folder_stat)) {
  436.      if (folder_stat.st_size == 0L)
  437.        unlink(folder_pathname);
  438.    }
  439.  
  440.    fclose(scb->wf);
  441.    scb->wf = NULL;
  442.    delete_msglist(scb->msg);
  443.    scb->msg=NULL;
  444.  
  445. }
  446.  
  447. static void near
  448. open_folder(struct pop_scb *scb) {
  449.   char folder_pathname[MAXPATH];
  450.   char *cp, line[BUF_LEN];
  451.   long pos;
  452.   FILE *fd;
  453.   struct pop_msg *msg;
  454.   struct stat folder_stat;
  455.  
  456.   sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  457.   scb->folder_len = 0;
  458.   scb->folder_file_size = 0;
  459.  
  460.   if (stat(folder_pathname,&folder_stat)){
  461.     usprintf(scb->socket,count_rsp,scb->folder_len);
  462.     scb->state  = TRANS;
  463.     return;     /* no file = OK */
  464.   }
  465.  
  466.   scb->folder_file_size = folder_stat.st_size;
  467.  
  468.   if ((fd = open_file(folder_pathname,"r",0,1)) == NULLFILE){
  469.     state_error(scb,"Unable to open mail folder");
  470.     return;
  471.   }
  472.  
  473.   if ((scb->wf = temp_file(0,1)) == NULL) {
  474.     state_error(scb,"Unable to create work folder");
  475.     fclose(fd);
  476.     return;
  477.   }
  478.  
  479.   /* create first element */
  480.   if ( (scb->msg=(struct pop_msg *)cxallocw(sizeof(struct pop_msg),1)) == NULL) {
  481.     fatal_error(scb,"Unable to create pointer list");
  482.     return;
  483.   }
  484.  
  485.   scb->msg->next=NULL;
  486.   msg=scb->msg;
  487.   msg->len=0;
  488.   msg->deleted=0;
  489.  
  490.   while(!feof(fd)) {
  491.     pos=ftell(scb->wf);
  492.     fgets(line,BUF_LEN,fd);
  493.  
  494.     /* scan for begining of a message */
  495.     if (isSOM(line)) {
  496.       scb->folder_len++;
  497.  
  498.       if( (msg->next=(struct pop_msg *)cxallocw(sizeof(struct pop_msg),1)) == NULL) {
  499.     fatal_error(scb,"Unable to create pointer list");
  500.     return;
  501.       }
  502.  
  503.       msg=msg->next;
  504.       msg->pos=pos;
  505.       msg->next=NULL;
  506.       msg->len=0;
  507.       msg->deleted=0;
  508.       pwait(NULL);
  509.  
  510.       /* now put the line in the work file */
  511.     }
  512.     fputs(line,scb->wf);
  513.     if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  514.       *cp = '\0';
  515.     if ( *line == '.' )
  516.       msg->len++;
  517.     msg->len +=strlen(line)+2; /* Add msg len count */
  518.   }
  519.  
  520.   fclose(fd);
  521.   scb->high_num=0;        /* reset high read */
  522.  
  523.   usprintf(scb->socket,count_rsp,scb->folder_len);
  524.   scb->state  = TRANS;
  525. }
  526.  
  527. static void near
  528. stat_message(struct pop_scb *scb) {
  529.   long total=0;
  530.   int count=0;
  531.   struct pop_msg *msg;
  532.  
  533.   if (scb == NULLSCB)     /* check for null -- wa6smn */
  534.     return;
  535.  
  536.   if (scb->folder_len)    /* add everything up */
  537.     for (msg=scb->msg->next; msg!=NULL; msg=msg->next)
  538.     if (!msg->deleted) {
  539.       total += msg->len; ++count;
  540.     }
  541.   usprintf(scb->socket,stat_rsp,count,total);
  542. }
  543.  
  544. static void near
  545. list_message(struct pop_scb *scb) {
  546.   struct pop_msg *msg;
  547.   int msg_no=0;
  548.   long total=0;
  549.   struct pop_msg *near goto_msg(struct pop_scb *,int );
  550.  
  551.   if (scb == NULLSCB) /* check for null -- wa6smn */
  552.     return;
  553.   if (scb->buf[sizeof(list_cmd) - 1] == ' ') {
  554.     msg_no = atoi(&(scb->buf[sizeof(list_cmd) - 1]));
  555.     msg=goto_msg(scb,msg_no);
  556.     if (msg==NULL || msg->deleted)
  557.       state_error(scb,"non existent or deleted message");
  558.     else
  559.       usprintf(scb->socket,list_single_rsp,msg_no,msg->len);
  560.   } else {
  561.     /* multiline */
  562.     if (scb->folder_len)        /* add everything */
  563.       for (msg=scb->msg->next; msg!=NULL;msg=msg->next)
  564.     if (!msg->deleted)
  565.       total += msg->len,++msg_no;
  566.  
  567.       usprintf(scb->socket,list_multi_rsp,msg_no,total);
  568.  
  569.     if (scb->folder_len)
  570.       for (msg=scb->msg->next,msg_no=1; msg!=NULL;msg=msg->next,msg_no++)
  571.       if (!msg->deleted) {
  572.     usprintf(scb->socket,"%d %ld\n",msg_no,msg->len);
  573.       }
  574.     usputs(scb->socket,multi_end_rsp);
  575.   }
  576. }
  577.  
  578. static void near
  579. retr_message(struct pop_scb *scb) {
  580.   char *cp,line[BUF_LEN];
  581.   long cnt;
  582.   int msg_no;
  583.   struct pop_msg *msg;
  584.   struct pop_msg *near goto_msg(struct pop_scb *,int );
  585.  
  586.   if (scb == NULLSCB) /* check for null -- wa6smn */
  587.     return;
  588.  
  589.   if (scb->buf[sizeof(retr_cmd) - 1] != ' ') {
  590.     state_error(scb,"no such message");
  591.     return;
  592.   }
  593.  
  594.   msg_no = atoi(&(scb->buf[sizeof(retr_cmd) - 1]));
  595.   msg=goto_msg(scb,msg_no);
  596.   if (msg==NULL || msg->deleted) {
  597.     state_error(scb,"no such message");
  598.     return;
  599.   }
  600.  
  601.   cnt  = msg->len;
  602.   usprintf(scb->socket,retr_rsp,cnt);
  603.   fseek(scb->wf,msg->pos,SEEK_SET);    /* Go there */
  604.  
  605.   while(!feof(scb->wf) && (cnt > 0)) {
  606.     fgets(line,BUF_LEN,scb->wf);
  607.     if((cp = strpbrk(line,"\r\n")) != NULLCHAR)
  608.       *cp = '\0';
  609.     if ( *line == '.' ) {
  610.       usputc(scb->socket,'.');
  611.       cnt--;
  612.     }
  613.     usputs(scb->socket,line);
  614.     usputc(scb->socket,'\n');
  615.     cnt -= (strlen(line)+2);        /* Compensate for CRLF */
  616.     pwait(NULL);
  617.   }
  618.  
  619.   usputs(scb->socket,multi_end_rsp);
  620.   if (msg_no >= scb->high_num)
  621.     scb->high_num=msg_no;          /* bump high water mark */
  622. }
  623.  
  624. static void near
  625. noop_message(struct pop_scb *scb) {
  626.   usprintf(scb->socket,noop_rsp);
  627. }
  628.  
  629. static void near
  630. last_message(struct pop_scb *scb) {
  631.   usprintf(scb->socket,last_rsp,scb->high_num);
  632. }
  633.  
  634. static void near
  635. rset_message(struct pop_scb *scb) {
  636.   struct pop_msg *msg;
  637.   long total=0;
  638.  
  639.   if (scb->folder_len)
  640.     for (msg=scb->msg->next; msg!=NULL; msg=msg->next)
  641.       msg->deleted=FALSE,total+=msg->len;
  642.  
  643.   scb->high_num=0;              /* reset last */
  644.   scb->folder_modified=FALSE;
  645.   usprintf(scb->socket,list_multi_rsp,scb->folder_len,total);
  646. }
  647.  
  648. static void near
  649. top_message(struct pop_scb *scb) {
  650.   char *ptr;
  651.   char line[BUF_LEN];
  652.   struct pop_msg *msg;
  653.   int msg_no=0,lines=0;
  654.   long total=0;
  655.   struct pop_msg *near goto_msg(struct pop_scb *,int );
  656.  
  657.   if (scb == NULLSCB) /* check for null -- wa6smn */
  658.     return;
  659.  
  660.   if (scb->buf[sizeof(top_cmd) - 1] != ' ') {
  661.     state_error(scb,"No message specified");
  662.     return;
  663.   }
  664.  
  665.   for (ptr=scb->buf+sizeof(top_cmd); *ptr==' ' ; ++ptr);
  666.       /* Space drop */
  667.   for ( ; *ptr!=' ' && *ptr !='\0'; ++ptr);
  668.       /* token drop */
  669.   msg_no = atoi(&(scb->buf[sizeof(top_cmd) - 1]));
  670.   lines = atoi(++ptr);  /* Get # lines to top */
  671.   if (lines < 0) lines=0;
  672.  
  673.   msg=goto_msg(scb,msg_no);
  674.   if (msg==NULL || msg->deleted)
  675.   {
  676.     state_error(scb,"non existent or deleted message");
  677.     return;
  678.   }
  679.   fseek(scb->wf,msg->pos,SEEK_SET);    /* Go there */
  680.   total=msg->len;            /* Length of current message */
  681.   usputs(scb->socket,noop_rsp);        /* Give OK */
  682.   do {
  683.     fgets(line,BUF_LEN,scb->wf);
  684.     if((ptr = strpbrk(line,"\r\n")) != NULLCHAR)
  685.       *ptr = '\0';
  686.     if ( *line == '.' ) {
  687.       usputc(scb->socket,'.');
  688.       total--;
  689.     }
  690.     total -= strlen(line)+2;
  691.     usputs(scb->socket,line);
  692.     usputc(scb->socket,'\n');
  693.   } while (*line!='\0' && total>0);
  694.  
  695.   for ( ; total > 0 && lines; --lines) {
  696.     fgets(line,BUF_LEN,scb->wf);
  697.     if((ptr = strpbrk(line,"\r\n")) != NULLCHAR)
  698.       *ptr = '\0';
  699.     if ( *line == '.' ) {
  700.       usputc(scb->socket,'.');
  701.       total--;
  702.     }
  703.     total -= strlen(line)+2;
  704.     usputs(scb->socket,line);
  705.     usputc(scb->socket,'\n');
  706.   }
  707.   usprintf(scb->socket,multi_end_rsp);
  708. }
  709.  
  710. static int near
  711. poplogin(char *username,char *pass) {
  712.    char buf[BUF_LEN], *cp, *cp1;
  713.    FILE *fp;
  714.  
  715.    if((fp = fopen(Popusers,READ_TEXT)) == NULLFILE) {
  716.      /* User file doesn't exist */
  717.      return(FALSE);
  718.    }
  719.  
  720.    while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  721.      if(buf[0] == '#')
  722.        continue;                 /* Comment */
  723.  
  724.      if((cp = strchr(buf,':')) == NULLCHAR)
  725.        continue;                /* Bogus entry */
  726.  
  727.      *cp++ = '\0';                /* Now points to password */
  728.      if(strcmp(username,buf) == 0)
  729.        break;                    /* Found user name */
  730.    }
  731.  
  732.    if(feof(fp)) {
  733.      /* User name not found in file */
  734.      fclose(fp);
  735.      return(FALSE);
  736.    }
  737.    fclose(fp);
  738.  
  739.    if ((cp1 = strchr(cp,':')) == NULLCHAR)
  740.      return(FALSE);
  741.  
  742.    *cp1 = '\0';
  743.    if(strcmp(cp,pass) != 0) {
  744.      /* Password required, but wrong one given */
  745.      return(FALSE);
  746.    }
  747.  
  748.    /* whew! finally made it!! */
  749.    return(TRUE);
  750. }
  751.  
  752. static void near
  753. dele_message(struct pop_scb *scb) {
  754.   struct pop_msg *msg;
  755.   int msg_no;
  756.   struct pop_msg *near goto_msg(struct pop_scb *,int );
  757.  
  758.   if (scb == NULLSCB) /* check for null -- wa6smn */
  759.     return;
  760.   if (scb->buf[sizeof(retr_cmd) - 1] != ' ') {
  761.     state_error(scb,"no such message");
  762.     return;
  763.   }
  764.   msg_no = atoi(&(scb->buf[sizeof(retr_cmd) - 1]));
  765.   msg=goto_msg(scb,msg_no);
  766.   if (msg==NULL || msg->deleted) {
  767.     state_error(scb,"attempt to access deleted message");
  768.     return;
  769.   }
  770.   if (msg->deleted) /* Don't bother if already dead */ {
  771.     state_error(scb,"message already deleted");
  772.     return;
  773.   }
  774.   msg->deleted=TRUE;
  775.   scb->folder_modified = TRUE;
  776.   usprintf(scb->socket,dele_rsp,msg_no);
  777. }
  778.  
  779.  
  780. static int near
  781. newmail(struct pop_scb *scb) {
  782.   char *folder_pathname;
  783.   struct stat folder_stat;
  784.   int s;
  785.  
  786.   folder_pathname = mxallocw(strlen(Mailspool) + strlen(scb->username) + 5);
  787.   sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  788.   s = stat(folder_pathname,&folder_stat);
  789.   xfree(folder_pathname);
  790.  
  791.   if(s) {
  792.     state_error(scb,"Unable to get old mail folder's status");
  793.     return(FALSE);
  794.   } else {
  795.     return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
  796.   }
  797. }
  798.  
  799.  
  800. static struct pop_msg * near
  801. goto_msg(struct pop_scb *scb,int msg_no)
  802. {
  803.   int msg_num;
  804.   struct pop_msg *msg;
  805.  
  806.   msg_num=msg_no-1;
  807.   if (scb->folder_len==0 || msg_num < 0)
  808.     return NULL;
  809.   for (msg=scb->msg->next; msg_num && msg!=NULL; --msg_num) msg=msg->next;
  810.   return msg;
  811. }
  812.  
  813. #endif /* POP3_SERVER */
  814.